//
//  MediaMergeUtils.swift
//  HappyToSee
//
//  Created by Wade on 2024/7/2.
//

import AVKit
import Foundation



class MediaMergeUtils {
    
    
    
    static func saveVideoToAlbum(filePath: String){
       
        UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, #selector(videoSaved(_:error:contextInfo:)), nil)
    }
     
    @objc static func videoSaved(_ videoPath: String, error: Error?, contextInfo: UnsafeRawPointer) {
        if let error = error {
            print("Error saving video: \(error.localizedDescription)")
        } else {
            print("Video was saved to the album.")
        }
    }
    
   static func conbindAudio(_ recoderAudioPathURL: URL?, _ recordVolume: Float, _ backMusicPathURL: URL?, _ backMusicVolume: Float, completed: @escaping (URL) -> Void, failed: @escaping (Error) -> Void) {
        // ********本地音频文件的url
       // 存放音频混合参数的数组
       var mixParams = [AVMutableAudioMixInputParameters]()
        //    将源文件转换为可处理的资源（初始资产），可理解为初次加工
        let originalAsset = AVURLAsset(url: recoderAudioPathURL!)
        let originalAsset2 = AVURLAsset(url: backMusicPathURL!)

        // ******想要合成音频文件，第一步，必须创建AVMutableComposition，类似于很多api这个后缀也为composition，意思可以理解为合成物，但不是最终产生的文件
        let composition = AVMutableComposition() // 可以看做轨道集合
        // 创建音频轨道素材----此处创建的为音轨属性，可以理解为合成物所需要的原料 ，对音轨的加工都在这个方法里面进行，此处为音频合成MediaType为 AVMediaTypeAudio
        let appendedAudioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
        let appendedAudioTrack2: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
        // 将初始资产再加工生成可用于拼接的（音轨资产） tracksWithMediaType()输出为[AVAssetTrack]数组取处于第一位的资源，也可能产物就是一个，只不过相当于用了个筐来盛放
        if let assetTrack1: AVAssetTrack = originalAsset.tracks(withMediaType: AVMediaType.audio).first, let assetTrack2: AVAssetTrack = originalAsset2.tracks(withMediaType: AVMediaType.audio).first {
            // 控制时间范围
            let timeRange1 = CMTimeRangeMake(start: CMTime.zero, duration: originalAsset2.duration)
            let timeRange2 = CMTimeRangeMake(start: CMTime.zero, duration: originalAsset2.duration)
            // ******音频合并，插入音轨文件，---音轨拼接（只要选好插入的时间点，插入的音轨资源可将多个assetTrack1（音轨资产）拼接到一起）
            try! appendedAudioTrack.insertTimeRange(timeRange1, of: assetTrack1, at: CMTime.zero)
            try! appendedAudioTrack2.insertTimeRange(timeRange2, of: assetTrack2, at: CMTime.zero) // 这里kCMTimeZero可以跟换一下，单必须是CMTime类型的，如果都是用kCMTimeZero，就会声音重叠
            // ********导出合并后的音频文件
         
            let recorderMix = AVMutableAudioMixInputParameters(track: appendedAudioTrack)
            recorderMix.setVolume(recordVolume, at: CMTime.zero)
            mixParams.append(recorderMix)
            let bgMusicMix = AVMutableAudioMixInputParameters(track: appendedAudioTrack2)
            bgMusicMix.setVolume(backMusicVolume, at: CMTime.zero)
            mixParams.append(bgMusicMix)

            let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)!
            // 这里就用到了上面的composition,composition作为原料加上下面的各种属性最终生成了指定路径下的目标文件
            let realPath = FilePathUtils.tempPath + "total.m4a" // 指定路径
            do {
                try FileManager.default.removeItem(atPath: realPath)
            }
            catch {
                NSLog("removeItem:"+error.localizedDescription+realPath)
            }
            // 创建一个可变的音频混音
            let audioMix = AVMutableAudioMix()
            // 将两个混音素材添加到混音对象中.
            audioMix.inputParameters = mixParams
            // 设置导出路径
            exportSession.outputURL = URL(fileURLWithPath: realPath)
            // 导出类型
            exportSession.outputFileType = AVFileType.m4a
            // 设置混音参数
            exportSession.audioMix = audioMix

            exportSession.exportAsynchronously {
                if exportSession.status == .completed {
                    completed(exportSession.outputURL!)
                } else if exportSession.status == .failed {
                    failed(exportSession.error!)
                }
            }
        }
    }

    /// 合并多段音频
    /// 生成一段包含多种轨道音乐的步骤:   exportsession -> AVAudioMix,AVMutableComposition -> AVMutableAudioMix -> [AVMutableAudioMixInputParameters] ->  AVMutableCompositionTrack.insert -> [AVAssetTrack] -> asset
    static func combineAllRecorder(_ recoderAudioPath: String?, _ recordVolume: Float, _ backMusicPath: String?, _ backMusicVolume: Float, completed: @escaping (String) -> Void, failed: @escaping (Error) -> Void) {
        let outputPath = FilePathUtils.tempPath + "combine.m4a"

        // 存放音频混合参数的数组
        var mixParams = [AVMutableAudioMixInputParameters]()

        // 用来添加track轨道的混合素材.
        let composition = AVMutableComposition()
        // 录音的轨道
        let recordMutableTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
        let insertTime = CMTime.zero // 下一次插入录音段的起点
        // 往录音轨道中添加所有录制的音频
        let recordURL = URL(string: recoderAudioPath!)

        let asset = AVURLAsset(url: recordURL!)
        // 取出资源中的音频素材
        if let track = asset.tracks(withMediaType: .audio).first {
            // 将音频素材插入到创建的录音轨道当中.
            try? recordMutableTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: asset.duration), of: track, at: insertTime)
            // 从录音轨道中生成一个混音素材,添加到数组中.
            let recorderMix = AVMutableAudioMixInputParameters(track: recordMutableTrack)
            recorderMix.setVolume(recordVolume, at: CMTimeMake(value: 0, timescale: 1))
            mixParams.append(recorderMix)
        }

        // 插入背景音乐
        if let backMusicPath = backMusicPath {
            let backMutableTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
            let bgAsset = AVURLAsset(url: URL(fileURLWithPath: backMusicPath))
            let bgTrack = bgAsset.tracks(withMediaType: .audio).first!
            try? backMutableTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: bgAsset.duration), of: bgTrack, at: CMTime.zero)
            let backTrackMix = AVMutableAudioMixInputParameters(track: backMutableTrack)
            // 背景音乐的音量
            backTrackMix.setVolume(backMusicVolume, at: CMTimeMake(value: 0, timescale: 1))
            mixParams.append(backTrackMix)
        }
        // 创建一个可变的音频混音
        let audioMix = AVMutableAudioMix()
        // 将两个混音素材添加到混音对象中.
        audioMix.inputParameters = mixParams

        let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)!
        exportSession.outputFileType = AVFileType.m4a
        // 如果有混音就设置这个参数.
        exportSession.audioMix = audioMix
        exportSession.outputURL = URL(string: outputPath)
        exportSession.exportAsynchronously {
            if exportSession.status == .completed {
                completed(outputPath)
            } else if exportSession.status == .failed {
                failed(exportSession.error!)
            }
        }
    }

    // 让视频消音
    func muteVideo(url: URL, completion: @escaping (AVMutableComposition?, Error?) -> Void) {
        let asset = AVAsset(url: url)
        let composition = AVMutableComposition()
        let videoTrack = asset.tracks(withMediaType: .video).first!

        // 复制视频轨道到新的复合片段中
        let videoTrackInsertTime = CMTime.zero
        let videoCompositionTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)!
        try? videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: asset.duration), of: videoTrack, at: videoTrackInsertTime)

        // 移除音频轨道
        let audioTrack = asset.tracks(withMediaType: .audio).first
        if let audioTrack = audioTrack {
            let audioTrackInsertTime = CMTime.zero
            let emptyAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
            try? emptyAudioTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: asset.duration), of: audioTrack, at: audioTrackInsertTime)
            let zeroVolume = AVMutableAudioMixInputParameters(track: emptyAudioTrack)
            zeroVolume.setVolume(0, at: CMTime.zero)
        }

        // 输出URL
        let outputUrl = URL(fileURLWithPath: NSTemporaryDirectory() + "output.mp4")
        let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)
        exporter?.outputURL = outputUrl
        exporter?.outputFileType = .mp4
        exporter?.exportAsynchronously {
            switch exporter?.status {
            case .some(.completed):
                completion(composition, nil)
            case .some(.failed), .some(.cancelled):
                completion(nil, exporter?.error)
            default:
                completion(nil, nil)
            }
        }
    }

    // 音频和视频合成
    static func mergeVideoWithAudio(videoUrl: URL, videoSize: CGSize, audioUrl: URL, success: @escaping ((URL) -> Void), failure: @escaping ((Error?) -> Void)) {
        let mixComposition = AVMutableComposition()
        var mutableCompositionVideoTrack: [AVMutableCompositionTrack] = []
        var mutableCompositionAudioTrack: [AVMutableCompositionTrack] = []
        let totalVideoCompositionInstruction = AVMutableVideoCompositionInstruction()

        let aVideoAsset = AVAsset(url: videoUrl)
        let aAudioAsset = AVAsset(url: audioUrl)

        if let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
            // 设置视频素材的合成音量
//            let videoVolumeMix = AVMutableAudioMixInputParameters(track: videoTrack)
//            // 在0秒开始的位置将音频设置为1
//            videoVolumeMix.setVolume(videoVolume, at: CMTime.zero)
            // 设置音频素材的合成音量
//            let audioVolumeMix = AVMutableAudioMixInputParameters(track: audioTrack)
//            // 在0秒开始的位置将音频设置为1
//            audioVolumeMix.setVolume(audioVolume, at: CMTime.zero)

            mutableCompositionVideoTrack.append(videoTrack)
            mutableCompositionAudioTrack.append(audioTrack)

            if let aVideoAssetTrack: AVAssetTrack = aVideoAsset.tracks(withMediaType: .video).first, let aAudioAssetTrack: AVAssetTrack = aAudioAsset.tracks(withMediaType: .audio).first {
                do {
                    try mutableCompositionVideoTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aVideoAssetTrack, at: CMTime.zero)
                    try mutableCompositionAudioTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aAudioAssetTrack.timeRange.duration), of: aAudioAssetTrack, at: CMTime.zero)
                    videoTrack.preferredTransform = aVideoAssetTrack.preferredTransform
                } catch {
                    print(error)
                }

                totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration)
            }
        }

        let mutableVideoComposition = AVMutableVideoComposition()
        // 视频帧率  1/30秒每帧
        mutableVideoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
        mutableVideoComposition.renderSize = videoSize

        let outputURL = URL(fileURLWithPath: FilePathUtils.cachePath!).appendingPathComponent("VideoCache/" + videoUrl.lastPathComponent)
        do {
            if FileManager.default.fileExists(atPath: outputURL.path) {
                try FileManager.default.removeItem(at: outputURL)
                FileManager.default.createFile(atPath: outputURL.absoluteString, contents: Data())
            } else {
                try FileManager.default.createDirectory(at: outputURL.deletingLastPathComponent(), withIntermediateDirectories: true)
                FileManager.default.createFile(atPath: outputURL.absoluteString, contents: Data())
            }
        } catch {
            print(error)
        }

        if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) {
            exportSession.outputURL = outputURL
            exportSession.outputFileType = AVFileType.mp4
            exportSession.shouldOptimizeForNetworkUse = true
            /// try to export the file and handle the status cases
            exportSession.exportAsynchronously(completionHandler: {
                switch exportSession.status {
                case .failed:
                    if let _error = exportSession.error {
                        failure(_error)
                    }
                case .cancelled:
                    if let _error = exportSession.error {
                        failure(_error)
                    }
                default:
                    print("finished")
                    success(outputURL)
                }
            })
        } else {
            failure(nil)
        }
    }

    // 音频和视频合成
    static func mergeVideoWithAudio(videoUrl: URL, videoVolume: Float, videoSize: CGSize, audioUrl: URL, audioVolume: Float, success: @escaping ((URL) -> Void), failure: @escaping ((Error?) -> Void)) {
        let mixComposition = AVMutableComposition()
        var mutableCompositionVideoTrack: [AVMutableCompositionTrack] = []
        var mutableCompositionAudioTrack: [AVMutableCompositionTrack] = []
        let totalVideoCompositionInstruction = AVMutableVideoCompositionInstruction()

        let aVideoAsset = AVAsset(url: videoUrl)
        let aAudioAsset = AVAsset(url: audioUrl)

        if let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
            // 设置视频素材的合成音量
//            let videoVolumeMix = AVMutableAudioMixInputParameters(track: videoTrack)
//            // 在0秒开始的位置将音频设置为1
//            videoVolumeMix.setVolume(videoVolume, at: CMTime.zero)
            // 设置音频素材的合成音量
//            let audioVolumeMix = AVMutableAudioMixInputParameters(track: audioTrack)
//            // 在0秒开始的位置将音频设置为1
//            audioVolumeMix.setVolume(audioVolume, at: CMTime.zero)

            mutableCompositionVideoTrack.append(videoTrack)
            mutableCompositionAudioTrack.append(audioTrack)

            if let aVideoAssetTrack: AVAssetTrack = aVideoAsset.tracks(withMediaType: .video).first, let aAudioAssetTrack: AVAssetTrack = aAudioAsset.tracks(withMediaType: .audio).first {
                do {
                    try mutableCompositionVideoTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aVideoAssetTrack, at: CMTime.zero)
                    try mutableCompositionAudioTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aAudioAssetTrack.timeRange.duration), of: aAudioAssetTrack, at: CMTime.zero)
                    videoTrack.preferredTransform = aVideoAssetTrack.preferredTransform
                } catch {
                    print(error)
                }

                totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration)
            }
        }

        let mutableVideoComposition = AVMutableVideoComposition()
        // 视频帧率  1/30秒每帧
        mutableVideoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
        mutableVideoComposition.renderSize = videoSize

        let outputURL = URL(fileURLWithPath: FilePathUtils.cachePath!).appendingPathComponent("VideoCache/" + videoUrl.lastPathComponent)
        do {
            if FileManager.default.fileExists(atPath: outputURL.path) {
                try FileManager.default.removeItem(at: outputURL)
                FileManager.default.createFile(atPath: outputURL.absoluteString, contents: Data())
            } else {
                try FileManager.default.createDirectory(at: outputURL.deletingLastPathComponent(), withIntermediateDirectories: true)
                FileManager.default.createFile(atPath: outputURL.absoluteString, contents: Data())
            }
        } catch {
            print(error)
        }

        if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) {
            exportSession.outputURL = outputURL
            exportSession.outputFileType = AVFileType.mp4
            exportSession.shouldOptimizeForNetworkUse = true
            /// try to export the file and handle the status cases
            exportSession.exportAsynchronously(completionHandler: {
                switch exportSession.status {
                case .failed:
                    if let _error = exportSession.error {
                        failure(_error)
                    }
                case .cancelled:
                    if let _error = exportSession.error {
                        failure(_error)
                    }
                default:
                    print("finished")
                    success(outputURL)
                }
            })
        } else {
            failure(nil)
        }
    }

    // 音频和视频合成2
    func mergeVideoWithAudio2(videoUrl: URL,
                              videoSize: CGSize,
                              audioUrl: URL,
                              success: @escaping ((URL) -> Void),
                              failure: @escaping ((Error?) -> Void))
    {
        let mixComposition = AVMutableComposition()
        var mutableCompositionVideoTrack: [AVMutableCompositionTrack] = []
        var mutableCompositionAudioTrack: [AVMutableCompositionTrack] = []
        let totalVideoCompositionInstruction = AVMutableVideoCompositionInstruction()

        let aVideoAsset = AVAsset(url: videoUrl)
        let aAudioAsset = AVAsset(url: audioUrl)

        if let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
            mutableCompositionVideoTrack.append(videoTrack)
            mutableCompositionAudioTrack.append(audioTrack)

            if let aVideoAssetTrack: AVAssetTrack = aVideoAsset.tracks(withMediaType: .video).first, let aAudioAssetTrack: AVAssetTrack = aAudioAsset.tracks(withMediaType: .audio).first {
                do {
                    try mutableCompositionVideoTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aVideoAssetTrack, at: CMTime.zero)

                    let videoDuration = aVideoAsset.duration
                    if CMTimeCompare(videoDuration, aAudioAsset.duration) == -1 {
                        try mutableCompositionAudioTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aAudioAssetTrack, at: CMTime.zero)
                    } else if CMTimeCompare(videoDuration, aAudioAsset.duration) == 1 {
                        var currentTime = CMTime.zero
                        while true {
                            var audioDuration = aAudioAsset.duration
                            let totalDuration = CMTimeAdd(currentTime, audioDuration)
                            if CMTimeCompare(totalDuration, videoDuration) == 1 {
                                audioDuration = CMTimeSubtract(totalDuration, videoDuration)
                            }
                            try mutableCompositionAudioTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aAudioAssetTrack, at: currentTime)

                            currentTime = CMTimeAdd(currentTime, audioDuration)
                            if CMTimeCompare(currentTime, videoDuration) == 1 || CMTimeCompare(currentTime, videoDuration) == 0 {
                                break
                            }
                        }
                    }
                    videoTrack.preferredTransform = aVideoAssetTrack.preferredTransform

                } catch {
                    print(error)
                }

                totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration)
            }
        }

        let mutableVideoComposition = AVMutableVideoComposition()
        mutableVideoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
        mutableVideoComposition.renderSize = videoSize

        if let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
            let outputURL = URL(fileURLWithPath: documentsPath).appendingPathComponent("\("fileName").m4v")
            do {
                if FileManager.default.fileExists(atPath: outputURL.path) {
                    try FileManager.default.removeItem(at: outputURL)
                }
            } catch {}

            if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) {
                exportSession.outputURL = outputURL
                exportSession.outputFileType = AVFileType.mp4
                exportSession.shouldOptimizeForNetworkUse = true

                // try to export the file and handle the status cases
                exportSession.exportAsynchronously(completionHandler: {
                    switch exportSession.status {
                    case .failed:
                        if let error = exportSession.error {
                            failure(error)
                        }

                    case .cancelled:
                        if let error = exportSession.error {
                            failure(error)
                        }

                    default:
                        print("finished")
                        success(outputURL)
                    }
                })
            } else {
                failure(nil)
            }
        }
    }
}
